﻿using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Linq;
using System;

namespace WPHttpServer
{
    public class HttpRequestParser
    {
        private readonly HttpServer _associatedServer;

        public HttpRequestParser(HttpServer associatedServer)
        {
            _associatedServer = associatedServer;
        }

        public HttpRequest Parse(byte[] httpRequestBytes)
        {
            HttpRequest httpRequest = new HttpRequest();

            string requestAsText = Encoding.UTF8.GetString(httpRequestBytes, 0, httpRequestBytes.Length);
            int contentStartIndex = requestAsText.IndexOf(Environment.NewLine + Environment.NewLine, System.StringComparison.Ordinal);

            string headersString = requestAsText.Substring(0, contentStartIndex - 1);
            string[] headers = headersString.Split(new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);

            if (headers.Any())
            {
                // read the headers
                string requestTypeUriAndProtocol = headers.First();
                ReadVerbUriProtocol(requestTypeUriAndProtocol, httpRequest);

                var otherHeaders = headers.Skip(1);

                foreach (string headerString in otherHeaders)
                {
                    string[] headerStringNameValueString = headerString.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries)
                                                                       .Select(s => s.Trim())
                                                                       .ToArray();

                    string headerName = headerStringNameValueString[0];
                    string headerValue = headerStringNameValueString[1];

                    httpRequest.Headers.Add(headerName, headerValue);
                }

                if (httpRequest.Headers.ContainsKey(HttpConstants.CONTENT_TYPE_HEADER_NAME))
                {
                    byte[] contentBytes = httpRequestBytes.Skip(contentStartIndex + 4).ToArray();
                    string contentTypeString = httpRequest.Headers[HttpConstants.CONTENT_TYPE_HEADER_NAME];
                    string[] contentTypeAndCharset = contentTypeString.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    string contentType = contentTypeAndCharset[0];                    

                    switch (contentType)
                    {
                        case HttpConstants.X_WWW_FORM_URLENCODED:
                        {
                            string paramsString = Encoding.UTF8.GetString(contentBytes,0, contentBytes.Length);

                            // decode the url characters
                            paramsString = HttpUtility.UrlDecode(paramsString);

                            string[] formFieldValues = paramsString.Split(new char[] {'&'}, StringSplitOptions.RemoveEmptyEntries);

                            Dictionary<string,string> formKeyValues = formFieldValues
                                .Select(formFieldValue => formFieldValue.Split(new char[] {'='}, StringSplitOptions.RemoveEmptyEntries))
                                .ToDictionary(formFieldValue2 => formFieldValue2[0], formFieldValue2 => formFieldValue2[1]);

                            httpRequest.Content = new RequestFormData(formKeyValues);
                            break;
                        }
                    } 
                }
            }
            else
            {
                throw new HttpException(HttpStatusCode.BadRequest);
            }

            return httpRequest;
        }

        private void ReadVerbUriProtocol(string requestTypeUriAndProtocol, HttpRequest httpRequest)
        {
            int indexOfSlash = requestTypeUriAndProtocol.IndexOf('/');

            // -1 because we want to remove the space after the verb
            string verb = requestTypeUriAndProtocol.Substring(0, indexOfSlash - 1);
            httpRequest.RequestType = (HttpRequestType) Enum.Parse(typeof (HttpRequestType), verb, ignoreCase: true);

            int indexOfUriEnd = requestTypeUriAndProtocol.IndexOf(' ', indexOfSlash);
            string uri = requestTypeUriAndProtocol.Substring(indexOfSlash, indexOfUriEnd - indexOfSlash + 1);
            httpRequest.Uri = new Uri("http://" + this._associatedServer.Domain + uri, UriKind.RelativeOrAbsolute);

            // the rest is the protocoll
            string protocol = requestTypeUriAndProtocol.Substring(indexOfUriEnd + 1);
            httpRequest.Protocol = protocol;
        }
    }
}